package com.cupcorn.lib.game;

import org.andengine.engine.camera.ZoomCamera;
import org.andengine.engine.camera.hud.HUD;
import org.andengine.engine.handler.IUpdateHandler;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.FillResolutionPolicy;
import org.andengine.entity.scene.IOnAreaTouchListener;
import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.ITouchArea;
import org.andengine.entity.scene.Scene;
import org.andengine.input.touch.TouchEvent;
import org.andengine.input.touch.controller.MultiTouch;
import org.andengine.input.touch.controller.MultiTouchController;
import org.andengine.input.touch.detector.ClickDetector;
import org.andengine.input.touch.detector.ClickDetector.IClickDetectorListener;
import org.andengine.input.touch.detector.ScrollDetector;
import org.andengine.input.touch.detector.ScrollDetector.IScrollDetectorListener;
import org.andengine.ui.activity.SimpleBaseGameActivity;
import org.andengine.util.color.Color;

import android.content.Context;
import android.os.Handler;
import android.view.KeyEvent;
import android.widget.Toast;

import com.cupcorn.lib.Utils;
import com.cupcorn.lib.define.iDefines;
import com.cupcorn.lib.display.DisplayFont;
import com.cupcorn.lib.game.element.GameElementFont;
import com.cupcorn.lib.game.element.GameElementMusic;
import com.cupcorn.lib.game.element.GameElementState;
import com.cupcorn.lib.player.PlayerMusic;
import com.cupcorn.lib.player.PlayerSound;

public abstract class GameEngine extends SimpleBaseGameActivity implements iDefines, IUpdateHandler, IOnSceneTouchListener, IClickDetectorListener,
	IScrollDetectorListener, IOnAreaTouchListener
{
	private static float			CAMERA_WIDTH, CAMERA_HEIGHT;
	
	boolean						gamePaused;
	
	private ScreenOrientation	ORIENTATION;
	
	public ZoomCamera			camera;
	public GameScene			scene;
	public HUD					hud		= new HUD();
	
	private GameElementState[]	state;
	public DisplayFont			displayFont;
	public PlayerMusic			playerMusic;
	public PlayerSound			playerSound;
	
	private ClickDetector		click;
	private ScrollDetector		scroll;
	public final Handler		handler	= new Handler();
	int							gameUpdateStep;
	
	public int					gameCurrentState, gameNextState;
	public int					messageCurrentState, messageNextState;
	
	long						waitTime, waitTimeElapsed, waitTimeRemaining;
	public long				startGameTimeMillis, systemTimeMillis, systemTimeMillisPauseHandler;
	long						systemTimeMillisPauseHandlerPaused, systemTimeMillisPauseHandlerResumed;
	
	private static GameEngine instance;
	
	public static GameEngine getInstance()
	{
		return instance;
	}
	
	public static float getGameWidth()
	{
		return CAMERA_WIDTH;
	}
	
	public static float getGameHeight()
	{
		return CAMERA_HEIGHT;
	}
	
	public EngineOptions onCreateEngineOptions()
	{
		CAMERA_WIDTH = this.getWindowManager().getDefaultDisplay().getWidth();
		CAMERA_HEIGHT = this.getWindowManager().getDefaultDisplay().getHeight();
		
		ORIENTATION = gameOrientation();
		
		startGameTimeMillis = System.currentTimeMillis();
		camera = new ZoomCamera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
		camera.setHUD(hud);
		final EngineOptions eo = new EngineOptions(true, ORIENTATION, new FillResolutionPolicy(), camera);
		eo.getAudioOptions().setNeedsMusic(true).setNeedsSound(true);
		return eo;
	}
	
	protected void onCreateResources()
	{
		mEngine.registerUpdateHandler(this);
		if (MultiTouch.isSupported(this)) mEngine.setTouchController(new MultiTouchController());
		
		state = gameState();
		
		if (gameFont() != null) displayFont = new DisplayFont(this, gameFont());
		if (gameMusic() != null) playerMusic = new PlayerMusic(this, gameMusic());
		if (gameSound() != null) playerSound = new PlayerSound(this, gameSound());
	}
	
	protected Scene onCreateScene()
	{
		scene = new GameScene(Color.WHITE);
		
		scene.setOnSceneTouchListener(this);
		scene.setOnAreaTouchListener(this);
		hud.setOnAreaTouchListener(this);
		
		click = new ClickDetector(this);
		scroll = new ScrollDetector(this);
		
		instance = this;
		
		return scene;
	}
	
	public synchronized void onPauseGame()
	{
		Utils.TRACE("OnPause");
		gamePaused = true;
		if (playerMusic != null) playerMusic.pauseAll();
		if (playerSound != null) playerSound.pauseAll();
		systemTimeMillisPauseHandlerPaused = System.currentTimeMillis();
		super.onPauseGame();
	}
	
	public synchronized void onResumeGame()
	{
		Utils.TRACE("OnResume");
		super.onResumeGame();
		if (playerMusic != null) playerMusic.resumeAll();
		if (playerSound != null) playerSound.resumeAll();
		systemTimeMillisPauseHandlerResumed = System.currentTimeMillis();
		if (gamePaused)
		{
			systemTimeMillisPauseHandler += systemTimeMillisPauseHandlerResumed - systemTimeMillisPauseHandlerPaused;
		}
	}
	
	public void onUpdate(float pSecondsElapsed)
	{
		SystemTimeMillisProcess();
		gamePaused = false;
		
		messageCurrentState = messageNextState;
		gameCurrentState = gameNextState;
		switch (messageCurrentState)
		{
			case MESSAGE_STATE_LOAD:
				if (state[gameCurrentState].load)
				{
					if (state[gameCurrentState].indexLoadState != null)
					{
						for (int i = 0; i < state[gameCurrentState].indexLoadState.length; i++)
							state[state[gameCurrentState].indexLoadState[i]].state.load();
					}
					state[gameCurrentState].load = false;
				}
				messageNextState = NextMessageState();
				break;
			
			case MESSAGE_STATE_INIT:
				state[gameCurrentState].state.init();
				messageNextState = NextMessageState();
				break;
			
			case MESSAGE_STATE_UPDATE:
				state[gameCurrentState].state.update(pSecondsElapsed);
				if (state[gameCurrentState].autoNextState) messageNextState = NextMessageState();
				break;
			
			case MESSAGE_STATE_EXIT:
				state[gameCurrentState].state.exit();
				if (state[gameCurrentState].autoNextState)
				{
					if (gameCurrentState != NextGameState())
					{
						messageNextState = NextMessageState();
						gameNextState = NextGameState();
					}
				}
				else
					messageNextState = NextMessageState();
				break;
		}
	}
	
	public void reset()
	{}
	
	public boolean onKeyUp(int keyCode, KeyEvent event)
	{
		if (state[gameCurrentState].keyUp) state[gameCurrentState].state.keyUp(keyCode, event);
		return false;
		// return super.onKeyUp(keyCode, event);
	}
	
	public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent)
	{
		if (state[gameCurrentState].sceneTouch)
		{
			state[gameCurrentState].state.sceneTouch(pScene, pSceneTouchEvent);
			
			if (state[gameCurrentState].click) click.onTouchEvent(pSceneTouchEvent);
			if (state[gameCurrentState].scroll) scroll.onTouchEvent(pSceneTouchEvent);
			
		}
		return false;
	}
	
	public boolean onAreaTouched(TouchEvent pSceneTouchEvent, ITouchArea pTouchArea, float pTouchAreaLocalX, float pTouchAreaLocalY)
	{
		if (state[gameCurrentState].areaTouch) state[gameCurrentState].state.onAreaTouched(pSceneTouchEvent, pTouchArea, pTouchAreaLocalX, pTouchAreaLocalY);
		return false;
	}
	
	public void onClick(ClickDetector pClickDetector, int pPointerID, float pSceneX, float pSceneY)
	{
		state[gameCurrentState].state.click(pClickDetector, pPointerID, pSceneX, pSceneY);
	}
	
	public void onScrollStarted(ScrollDetector pScollDetector, int pPointerID, float pDistanceX, float pDistanceY)
	{
		state[gameCurrentState].state.scrollStarted(pScollDetector, pPointerID, pDistanceX, pDistanceY);
	}
	
	public void onScroll(ScrollDetector pScollDetector, int pPointerID, float pDistanceX, float pDistanceY)
	{
		state[gameCurrentState].state.Scroll(pScollDetector, pPointerID, pDistanceX, pDistanceY);
	}
	
	public void onScrollFinished(ScrollDetector pScollDetector, int pPointerID, float pDistanceX, float pDistanceY)
	{
		state[gameCurrentState].state.scrollFinished(pScollDetector, pPointerID, pDistanceX, pDistanceY);
	}
	
	public abstract ScreenOrientation gameOrientation();
	
	protected abstract GameElementState[] gameState();
	
	protected abstract GameElementFont[] gameFont();
	
	protected abstract GameElementMusic[] gameMusic();
	
	protected abstract GameElementMusic[] gameSound();
	
	private void SystemTimeMillisProcess()
	{
		systemTimeMillis = System.currentTimeMillis() - (startGameTimeMillis + systemTimeMillisPauseHandler);
	}
	
	public void WaitTimeCapture()
	{
		waitTime = System.currentTimeMillis();
	}
	
	public boolean IsTimePassed(long ms)
	{
		waitTimeElapsed = System.currentTimeMillis() - waitTime;
		waitTimeRemaining = ms - waitTimeElapsed;
		if (waitTime + ms <= System.currentTimeMillis())
		{
			waitTimeElapsed = 0;
			waitTimeRemaining = 0;
			
			return true;
		}
		return false;
	}
	
	public boolean IsCurrentTimeShorterThanCaptured(long ms)
	{
		waitTimeElapsed = System.currentTimeMillis() - waitTime;
		waitTimeRemaining = ms - waitTimeElapsed;
		if (waitTime + ms > System.currentTimeMillis())
		{
			waitTimeElapsed = 0;
			waitTimeRemaining = 0;
			
			return true;
		}
		return false;
	}
	
	public int NextMessageState()
	{
		int next = messageCurrentState + 1;
		if (next > iDefines.MESSAGE_STATE_EXIT) { return iDefines.MESSAGE_STATE_LOAD; }
		return next;
	}
	
	public int NextGameState()
	{
		if (gameCurrentState + 1 >= state.length)
		{
			Utils.TRACE("Next state null");
			return gameCurrentState;
		}
		else
			return gameCurrentState + 1;
	}
	
	public void nextState(int gameNextState, int messageNextState)
	{
		this.gameNextState = gameNextState;
		this.messageNextState = messageNextState;
	}
	
	public void SetGameScene(GameScene scene)
	{
		mEngine.setScene(scene);
	}
	
	public void ToastMakeText(long delayMillis, String text, int duration)
	{
		final String _text = text;
		final int _duration = duration;
		final Context context = this;
		handler.postDelayed(new Runnable()
		{
			@Override
			public void run()
			{
				Toast.makeText(context, _text, _duration).show();
			}
		}, delayMillis);
	}
	
}
